package tech.ferris.shepherd.common.utils;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang.CharEncoding;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

import lombok.NonNull;

/**
 * @author ferris
 */
public class EncryptionTool {

    private static final String AES_KEY_ALGORITHM = "AES";
    private static final String AES_CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding";
    private static final int AES_CIPHER_BIT = 256;

    private static final String RSA_KEY_ALGORITHM = "RSA";
    private static final String RSA_ALGORITHM_MODE_ECB = "RSA/ECB/PKCS1Padding";
    private static final int RSA_CIPHER_BIT = 1024;
    public static final String RSA_PUBLIC_KEY = "RSAPublicKey";
    public static final String RSA_PRIVATE_KEY = "RSAPrivateKey";

    /**
     * 生成AES密钥
     *
     * @return String 密钥
     */
    public static String generateAesKey() throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        KeyGenerator kg = KeyGenerator.getInstance(AES_KEY_ALGORITHM);
        kg.init(AES_CIPHER_BIT);
        SecretKey secretKey = kg.generateKey();
        return Base64.toBase64String(secretKey.getEncoded());
    }

    /**
     * AES加密数据
     *
     * @param data 待加密数据
     * @param key  密钥
     * @return String 加密后的数据
     */
    public static String encryptAes(@NonNull String key, String data) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(Base64.decode(key), AES_KEY_ALGORITHM));
        return Base64.toBase64String(cipher.doFinal(data.getBytes(CharEncoding.UTF_8)));
    }

    /**
     * AES解密数据
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return String 解密后的数据
     */
    public static String decryptAes(@NonNull String key, String data) throws Exception {
        byte[] dataByte = Base64.decode(data);
        Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decode(key), AES_KEY_ALGORITHM));
        return new String(cipher.doFinal(dataByte));
    }

    /**
     * 生成AES密钥
     *
     * @param seed 生成RAS密钥的种子字符串
     * @return Map 存储公钥和私钥的Map
     */
    public static Map<String, String> generateRsaKey(@NonNull String seed) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_KEY_ALGORITHM);
        keyPairGenerator.initialize(RSA_CIPHER_BIT, new SecureRandom(seed.getBytes(CharEncoding.UTF_8)));
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        String rsaPublicKeyString = Base64.toBase64String(rsaPublicKey.getEncoded());
        String rsaPrivateKeyString = Base64.toBase64String(rsaPrivateKey.getEncoded());
        //公私钥对象存入map中
        Map<String, String> keyMap = new HashMap<>(2);
        keyMap.put(RSA_PUBLIC_KEY, rsaPublicKeyString);
        keyMap.put(RSA_PRIVATE_KEY, rsaPrivateKeyString);
        return keyMap;
    }

    /**
     * RSA私钥加密
     *
     * @param key  私钥字符串
     * @param data 需要加密的数据字符串
     * @return String 密文
     */
    public static String encryptRsaByPrivateKey(@NonNull String key, String data) throws Exception {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM_MODE_ECB);
        cipher.init(Cipher.ENCRYPT_MODE, rsaPrivateKey);
        byte[] output = cipher.doFinal(data.getBytes(CharEncoding.UTF_8));
        return Base64.toBase64String(output);
    }

    /**
     * RSA公钥解密
     *
     * @param key  公钥字符串
     * @param data 需要解密的数据字符串
     * @return String 明文
     */
    public static String decryptRsaByPublicKey(@NonNull String key, String data) throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM_MODE_ECB);
        cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
        byte[] output = cipher.doFinal(Base64.decode(data));
        return new String(output);
    }

    /**
     * RSA公钥加密
     *
     * @param key  公钥字符串
     * @param data 需要加密的数据字符串
     * @return String 密文
     */
    public static String encryptRsaByPublicKey(@NonNull String key, String data) throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM_MODE_ECB);
        cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
        return Base64.toBase64String(cipher.doFinal(data.getBytes(CharEncoding.UTF_8)));
    }

    /**
     * RSA私钥解密
     *
     * @param key  私钥字符串
     * @param data 需要解密的数据字符串
     * @return String 明文
     */
    public static String decryptRsaByPrivateKey(@NonNull String key, String data) throws Exception {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM_MODE_ECB);
        cipher.init(Cipher.DECRYPT_MODE, rsaPrivateKey);
        return new String(cipher.doFinal(Base64.decode(data)));
    }

}
